#include <iostream>
#include "../CompressAlign/cpp/mian_util.h"
#include "../CompressAlign/cpp/compress_gpu.h"
#include "../CompressAlign/cpp/util.h"
#include "../cpp/cuda_header.h"
using namespace std;

uint32 getRefPos(uint3* SA_result, uint32 numSA, uint32* saIndex,uint32 numAlign, uint32* queryId,uint32* offset, uint32* pos)
{
	uint32 i,j, k;
	k = 0;
	for(i=0; i<numSA && k <numAlign; i++){
		queryId[i] = SA_result[i].x;
		offset[i] = k;
		for(j=SA_result[i].y; j<=SA_result[i].z && k<numAlign; j++) {
			pos[k] = saIndex[j];
			k++;
		}
	}
	printf("num alignment: %u\n", k);
	return k;
}

void getRefPosResult(uint3* cpuSA, uint32 numSA, uint32 nresult, uint32 textLength, uint32** queryId0,uint32** offset0, uint32** pos0, char* ref_name)
{
	char saFileName[200];// = "/home/luo_group/data/human_ref/ref_good/ref_good.fa.index.sa";
	const char* saFileSuffix = ".sa";
	strcat(saFileName, ref_name);
	strcat(saFileName,saFileSuffix);

	FILE* saFile = fopen(saFileName, "rb");
	uint32 saInterval;
	uint32 timer = 0, i=0;

	for(i=0; i<6; i++)
		fread(&saInterval, sizeof(int), 1, saFile);

	uint32 saIndexSize = (textLength + saInterval - 1) / saInterval;
	uint32* saIndex = (uint32*)malloc(saIndexSize * sizeof(int));
	uint32* queryId =  (uint32*)malloc(sizeof(int) * numSA);
	uint32* offset =  (uint32*)malloc(sizeof(int) * numSA);
	uint32* pos =  (uint32*)malloc(sizeof(int) * nresult);

	startTimer(&timer);
	fread(saIndex, sizeof(int), saIndexSize, saFile);
	endTimer(&timer, "load sa from disk");
	fclose(saFile);

	startTimer(&timer);
	getRefPos(cpuSA, numSA, saIndex, nresult, queryId, offset, pos);
	endTimer(&timer, "get ref pos ");
	free(saIndex);

	(*queryId0) = queryId;
	(*offset0) = offset;
	(*pos0) = pos;	
}

void compressQueryId(uint32* queryId, uint32 dataSize,uint32 numQuery, uint32* outId, uint32* outLen, uint32* outDataSize)
{
	uint32 *d_queryId, *d_queryId_len, *c_d_queryId, outSize;

	gpu_malloc(&d_queryId, dataSize * sizeof(int));
	TOGPU(d_queryId, queryId, dataSize * sizeof(int));
	gpu_malloc(&c_d_queryId, dataSize * sizeof(int));
	gpu_malloc(&d_queryId_len, dataSize * sizeof(int));

	INIT_TIMER;
	START_TIMER;

	gpu_delta_encoding(d_queryId, dataSize);

	gpu_run_length_encoding(d_queryId, dataSize, c_d_queryId, d_queryId_len, &outSize);

	END_TIMER;
	PRINT_TIMER_SEC("delta + rle kernel: ");

	FROMGPU(outId, c_d_queryId, sizeof(int)*outSize);
	FROMGPU(outLen,d_queryId_len, sizeof(int)*outSize);

	FREEGPU(&c_d_queryId);
	FREEGPU(&d_queryId_len);
	FREEGPU(&d_queryId);

	(*outDataSize) = outSize;
}

void decompressQueryId(uint32* in, uint32* in_len, uint32 dataSize, uint32* out, uint32* outSize)
{
	uint32 maxOutSize = (*outSize);
	uint32* d_out, *d_in, *d_in_len;
	gpu_malloc(&d_out, maxOutSize * sizeof(int));
	gpu_malloc(&d_in, dataSize * sizeof(int));
	TOGPU(d_in, in, dataSize*sizeof(int));
	gpu_malloc(&d_in_len, dataSize * sizeof(int));
	TOGPU(d_in_len, in_len, dataSize*sizeof(int));

	INIT_TIMER;
	START_TIMER;

	gpu_run_length_decoding(d_in, d_in_len, dataSize, d_out, outSize);
	gpu_delta_decoding(d_out, *outSize);

	END_TIMER;
	PRINT_TIMER_SEC("delta + rle decoding kernel: ");

	FROMGPU(out, d_out, sizeof(int)*(*outSize));

	FREEGPU(&d_in);
	FREEGPU(&d_in_len);
	FREEGPU(&d_out);
}

void outputIdPos(uint32* c_queryId, uint32* c_queryId_len, uint32 compressedSize, uint32* offset,uint32 numSA, 
		uint32* pos, uint32 nresult, FILE* resultFile, uint32* totalOutputSize)
{
	fwrite(&numSA1, sizeof(int), 1, resultFile);
	fwrite(&nresult, sizeof(int), 1, resultFile);
	fwrite(&compressedSize, sizeof(int), 1, resultFile);
	fwrite(c_queryId, sizeof(int), compressedSize, resultFile);
	fwrite(c_queryId_len, sizeof(int), compressedSize, resultFile);
	fwrite(offset, sizeof(int), numSA1, resultFile);
	fwrite(pos, sizeof(int), nresult, resultFile);

	(*totalOutputSize) += ( compressedSize * 2 + numSA1 + nresult )*4;

	printf("compressed queryid arary: %u bytes\n", compressedSize * 2 * 4);
	printf("offset arary: %u bytes\n", numSA1* 4);
	printf("pos arary: %u bytes\n", nresult * 4);
}

void testCompress()
{

	uint32 numSA, nresult,i, timer, textLength;
	const uint32 numQuery = 2000000;
	uint32 compressedSize = numQuery;
	uint32 *c_queryId, *c_queryId_len;
	INIT_TIMER;

	FILE* resultFile = fopen("pos_id_result", "rb");
	if(resultFile == NULL) {
		printf("can't open pos_id_result\n");
		return;
	}

	fread(&nresult, sizeof(int), 1, resultFile);
	fread(&numSA, sizeof(int), 1, resultFile);

	printf("numSA: %u, numAlign: %u\n", numSA, nresult);
	uint32* queryId =  (uint32*)malloc(sizeof(int) * numSA);
	uint32* offset =  (uint32*)malloc(sizeof(int) * numSA);
	uint32* pos =  (uint32*)malloc(sizeof(int) * nresult);

	fread(queryId, sizeof(int), numSA, resultFile);
	fread(offset, sizeof(int), numSA, resultFile);
	fread(pos, sizeof(int), nresult, resultFile);
	/*
	   uint32 k = 0;
	   for(int i =0; i<numSA-1; i++) {
	   for(int j=0; j<(offset[i+1]-offset[i]); j++) {
	   queryId[k++] = queryId_off[i];
	   }
	   }
	   for(int j=0; j<(nresult-offset[numSA-1]); j++) {
	   queryId[k++] = queryId_off[i];
	   }

	   printf("queryId size: %u\n", k);
	   */
	c_queryId = (uint32*)malloc(numQuery * sizeof(int));
	c_queryId_len = (uint32*)malloc(numQuery * sizeof(int));

	START_TIMER;
	compressQueryId(queryId, numSA, numQuery, c_queryId, c_queryId_len, &compressedSize);
	printf("compressed queryId size: %u\n", compressedSize);

	END_TIMER;
	PRINT_TIMER_SEC("rle time for queryId ");

	START_TIMER;
	uint32 *out = (uint32*)malloc(sizeof(int) * nresult);
	uint32 outSize = nresult;
	decompressQueryId(c_queryId, c_queryId_len, compressedSize, out, &outSize);

	END_TIMER;
	PRINT_TIMER_SEC("de-rle time for queryId ");

	for(uint32 i = 0; i<numSA; i++) {
		if(out[i] != queryId[i]) {
			printf("i: %u, out[i]: %u, queryId[i]: %u\n", i, out[i], queryId[i]);
			break;
		}
	}

	free(out);
	free(queryId);
	free(offset);
	free(pos);
}

void testGetRefPos()
{
	uint32 numSA, nresult,i, timer, textLength;
	uint3* cpuSA;
	FILE* saResultFin = fopen("../saInterval", "rb");
	FILE* resultFile = fopen("pos_id_result", "wb");

	fread(&numSA, sizeof(int), 1, saResultFin);
	fread(&nresult, sizeof(int), 1, saResultFin);
	fread(&textLength, sizeof(int), 1, saResultFin);
	cpuSA = (uint3*)malloc(sizeof(uint3) * numSA);
	fread(cpuSA, sizeof(uint3), numSA, saResultFin);
	fclose(saResultFin);

	printf("numSA: %u, numAlign: %u, textLength: %u\n", numSA, nresult, textLength);

	uint32* queryId, *offset, *pos;
	getRefPosResult(cpuSA, numSA, nresult, textLength, &queryId, &offset, &pos);

	printf("write infomation out...\n");
	startTimer(&timer);
	fwrite(&nresult, sizeof(int), 1, resultFile);
	fwrite(&numSA, sizeof(int), 1, resultFile);
	fwrite(queryId, sizeof(int), numSA, resultFile);
	fwrite(offset, sizeof(int), numSA, resultFile);
	fwrite(pos, sizeof(int), nresult, resultFile);
	endTimer(&timer, "write queryId and pos to disk ");

	free(cpuSA);
	free(pos);
	free(offset);
	free(queryId);
	fclose(resultFile);


}


int main()
{

	testCompress();

	return 0;
}
